﻿using DLog.Model;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DLog
{
    public class SyncLogger :Logger, ILogger
    {
        private static object _lock = new object();
        private static Thread _thread;
        private Queue<LogModel> _queue;//log queue
        private SyncEvents _syncEvents;

        public SyncLogger()
        {
            _queue = new Queue<LogModel>();
            _syncEvents = new SyncEvents();
        }

        /// <summary>
        /// add log to queue
        /// </summary>
        /// <param name="obj"></param>
        private void AddLog(Object obj)
        {
            LogModel log = obj as LogModel;
            if (!_syncEvents.ExitThreadEvent.WaitOne(0, false))
            {
                lock (((ICollection)_queue).SyncRoot)
                {
                    _queue.Enqueue(log);
                    _syncEvents.NewItemEvent.Set();
                }
            }
        }

        /// <summary>
        /// add log
        /// </summary>
        /// <param name="log"></param>
        public void Add(LogModel log)
        {
            Thread t = new Thread(AddLog);
            t.Start(log);
        }

        /// <summary>
        /// save logs to storage
        /// </summary>
        private void Save()
        {
            int flag = 0;
            while (flag >= 0)
            {
                if (_queue.Count == 0)
                {
                    flag = WaitHandle.WaitAny(_syncEvents.EventArray);
                    if (flag == 1)
                    {
                        flag = -1;
                    }
                }
                lock (((ICollection)_queue).SyncRoot)
                {
                    if (_queue.Count > 0)
                    {
                        LogModel log = _queue.Dequeue();
                        Dal.Insert(log);
                    }
                }
            }
            //if (_queue.Count < 0)
            //{
            //    _syncEvents.ExitThreadEvent.Set();
            //}
        }

        /// <summary>
        /// run save
        /// </summary>
        public void Run()
        {
            if (_thread == null || !_thread.IsAlive)
            {
                _thread = new Thread(Save);
                _thread.Start();
            }
        }

        /// <summary>
        /// stop thread
        /// </summary>
        public void Stop()
        {
            _syncEvents.ExitThreadEvent.Set();
        }

        #region override the base class function record function

        public override bool Log(string meesage, int logLevel)
        {
            LogModel lm = new Model.LogModel()
            {
                CreateDate = DateTime.Now,
                LogLevel = logLevel,
                Message = meesage,
                StackTrade = string.Empty,
                Origin = string.Empty
            };
            this.Add(lm);
            this.Run();
            return true;
        }

        public override bool Log(Exception ex, int logLevel)
        {
            LogModel lm = new Model.LogModel()
            {
                CreateDate = DateTime.Now,
                LogLevel = logLevel,
                Message = ex.Message,
                StackTrade = ex.StackTrace,
                Origin = ex.Source
            };
            this.Add(lm);
            this.Run();
            return true;
        }

        public override bool Log(string meesage, Exception ex, int logLevel)
        {
            LogModel lm = new Model.LogModel()
            {
                CreateDate = DateTime.Now,
                LogLevel = logLevel,
                Message = meesage + "\t\nException:" + ex.Message,
                StackTrade = ex.StackTrace,
                Origin = ex.Source
            };
            this.Add(lm);
            this.Run();
            return true;
        }
        #endregion
    }
}
